We have used SingleR and scANVI/scArches to
perform cell type annotation on SCPCP000004 samples using
the NBAtlas reference. The goal of this notebook is to
compare these two sets of inferences to determine if they generally
agree.
Setup
suppressPackageStartupMessages({
library(ggplot2)
library(patchwork)
library(SingleCellExperiment)
})
theme_set(theme_bw())
umap_theme <- list(
theme(
aspect.ratio = 1,
legend.position = "bottom",
axis.text = element_blank(),
axis.ticks = element_blank()
)
)
set.seed(2025) # used for some viz
# Define color ramp for shared use in the heatmaps
heatmap_col_fun <- circlize::colorRamp2(c(0, 1), colors = c("white", "darkslateblue"))
# Set heatmap padding option
ComplexHeatmap::ht_opt(TITLE_PADDING = grid::unit(0.6, "in"))
# settings
options(
dplyr.summarise.inform = FALSE,
readr.show_col_types = FALSE
)
Paths
# The base path for the OpenScPCA repository, found by its (hidden) .git directory
repository_base <- rprojroot::find_root(rprojroot::is_git_root)
data_dir <- file.path(repository_base, "data", "current", "SCPCP000004")
merged_dir <- file.path(repository_base, "data", "current", "results", "merge-sce", "SCPCP000004")
module_dir <- file.path(repository_base, "analyses", "cell-type-neuroblastoma-04")
ref_dir <- file.path(module_dir, "references")
results_dir <- file.path(module_dir, "results")
singler_dir <- file.path(results_dir, "singler")
scanvi_dir <- file.path(results_dir, "scanvi")
# merged SCE file
sce_file <- file.path(
merged_dir,
"SCPCP000004_merged.rds"
)
# SingleR files
singler_files <- list.files(
path = singler_dir,
pattern = "_singler-annotations\\.tsv",
recursive = TRUE,
full.names = TRUE
) |>
# add names as library id
purrr::set_names(
\(x) {
stringr::str_remove(basename(x), "_singler-annotations.tsv")
}
)
# scanvi predictions
scanvi_file <- file.path(
scanvi_dir,
"scanvi-predictions.tsv"
)
# broad consensus cell type groups
validation_url <- "https://raw.githubusercontent.com/AlexsLemonade/OpenScPCA-analysis/refs/heads/main/analyses/cell-type-consensus/references/consensus-validation-groups.tsv"
# palette files
recoded_palette_file <- file.path(
module_dir,
"palettes",
"harmonized-cell-type-palette.tsv"
)
nbatlas_palette_file <- file.path(
module_dir,
"palettes",
"nbatlas-cell-type-palette.tsv"
)
# label mapping
nbatlas_label_map_file <- file.path(ref_dir, "nbatlas-label-map.tsv")
# sample metadata
sample_metadata_file <- file.path(data_dir, "single_cell_metadata.tsv")
Functions
# Source Jaccard and heatmap utilities functions
source(file.path(module_dir, "scripts", "utils", "jaccard-utils.R"))
# Source additional utilities functions:
# - subset_nbatlas_markers()
# - harmonize_celltypes()
# - faceted_umap()
# - generate_dotplot()
source(file.path(module_dir, "scripts", "utils", "celltype-utils.R"))
scANVI posterior probabilities
Before comparing to SingleR, we should get a sense of
reliability for the scANVI results. scANVI
returns a cell-specific posterior probability for each label, so we’ll
begin by looking at these distributions.
First, what is the distribution of posterior probabilities for
assigned labels? We’ll make a histogram below using a very visible color
to ensure the plot is legible, where each panel is a library, and
libraries are ordered by size.
scanvi_df |>
tidyr::separate(cell_id, into = c("library_id", "barcode")) |>
dplyr::add_count(library_id) |>
dplyr::mutate(
library_id = glue::glue("{library_id} (N={n})"),
library_id = factor(library_id),
library_id = forcats::fct_infreq(library_id),
library_id = forcats::fct_rev(library_id)
) |>
############ into ggplot ################
ggplot() +
aes(x = scanvi_posterior) +
# ugly but _very visible_ color
geom_histogram(bins = 100, fill = "magenta", color = "magenta") +
facet_wrap(vars(library_id), scales = "free_y", ncol = 7)

Across libraries, the median posterior probability is always
exceptionally high which suggests scANVI confidence is
roughly the same across libraries.
Let’s instead look at these probabilities on a per-cell type basis,
this time with a ridge plot including a line for the median value for
each distribution:
scanvi_ridgeplot <- scanvi_df |>
dplyr::group_by(scanvi) |>
dplyr::mutate(scanvi = glue::glue("{scanvi} (n={dplyr::n()})")) |>
########### into ggplot ############
ggplot() +
aes(y = forcats::fct_infreq(scanvi), x = scanvi_posterior) +
# show the median
ggridges::stat_density_ridges(quantile_lines = TRUE, quantiles = 2, alpha = 0.7) +
labs(x = "posterior probability", y = "scANVI label (number of cells)")
scanvi_ridgeplot

While annotation reliability is about the same on a per-library
basis, it seems that certain cell types may be much easier to classify
than others which may be a factor of how well represented they are in
the NBAtlas reference. There are very few cells in general
associated with the cell types that have very low median posterior
probabilities (NKT cell, Migratory cDC,
Circulating NK cell).
A 0.75 threshold seems like it may be a good
middle-ground for preserving labels while removing low-confidence
annotations. What fraction of cells will this remove?
sum(scanvi_df$scanvi_posterior < 0.75) / nrow(scanvi_df)
[1] 0.01886238
What are those cell types?
scanvi_df |>
dplyr::filter(scanvi_posterior < 0.75) |>
dplyr::count(scanvi) |>
dplyr::arrange(desc(n))
Notably, none are Neuroendocrine (the tumor type).
We’ll apply the 0.75 threshold below by recoding these cells to
Unknown.
pp_threshold <- 0.75
scanvi_df <- scanvi_df |>
dplyr::mutate(scanvi = ifelse(
scanvi_posterior < pp_threshold,
"Unknown",
scanvi
))
Compare labels
We’ll create a single data frame with UMAP coordinates and all cell
types (consensus, SingleR, and scANVI). We’ll
do this at two levels of organization:
- One with harmonized labels to be able to compare to consensus
- One with original NBAtlas labels to just compare
SingleR to scANVI
celltype_df <- scuttle::makePerCellDF(
merged_sce,
use.coldata = c("sample_id", "is_xenograft", "consensus_celltype_annotation"),
use.dimred = c("UMAP")
) |>
# there ARE repeated barcodes so we need to keep cell_id around
tibble::rownames_to_column("cell_id") |>
dplyr::rename(
UMAP1 = UMAP.1,
UMAP2 = UMAP.2,
consensus_annotation = consensus_celltype_annotation
) |>
dplyr::left_join(validation_df, by = "consensus_annotation") |>
# recode consensus NAs to "Unknown"
dplyr::mutate(
validation_group_annotation = ifelse(
is.na(validation_group_annotation), "Unknown", validation_group_annotation
)
) |>
dplyr::select(-consensus_annotation) |>
# merge in the annotations
dplyr::left_join(singler_df, by = "cell_id") |>
dplyr::left_join(scanvi_df, by = "cell_id") |>
# make it tidy
tidyr::pivot_longer(
c(validation_group_annotation, singler, scanvi),
names_to = "celltype_method",
values_to = "label"
) |>
harmonize_celltypes(label, label_recoded) |>
dplyr::mutate(label = ifelse(label == "NE", "Neuroendocrine", label)) |>
tidyr::separate(cell_id, into = c("library_id", "barcode"), remove = FALSE)
UMAP
This section displays the UMAP for the merged object (not
integrated) with cell type labels across methods. We show a panel for
each cell type method.
SingleR vs scANVI
This set of UMAPs shows annotation results from SingleR
and scANVI.
Since there are many cell types in this plot, it has been colored so
that all cell types in a given “family” each share a color, where we
consider these family groupings:
- T cells
- Natural killer cells
- Conventional dendritic cells
- Monocytes
label_order <- nbatlas_label_map |>
dplyr::add_count(NBAtlas_family) |>
dplyr::arrange(desc(n), NBAtlas_family) |>
dplyr::pull(NBAtlas_label)
label_order <- c(label_order, "Unknown")
umap_direct_df <- celltype_df |>
dplyr::filter(celltype_method != "validation_group_annotation") |>
# shuffle points to ensure order they are plotted is random
dplyr::sample_frac(1) |>
# order celltypes so that families are grouped together for a cleaner legend
# note this will give warnings if any of those cells aren't in the annotation, but that's fine
dplyr::mutate(label = factor(label, levels = label_order))
ggplot(umap_direct_df) +
aes(x = UMAP1, y = UMAP2, color = label) +
geom_point(alpha = 0.5, size = 0.2) +
scale_color_manual(values = nbatlas_celltype_pal) +
facet_wrap(vars(celltype_method)) +
umap_theme +
guides(color = guide_legend(override.aes = list(size = 2, alpha = 1)))

Cell type annotations look broadly consistent between these methods,
although SingleR appears to assign more heterogeneity
within the “clumps” of cells (which likely correspond to individual
libraries in this merged object).
Harmonized labels for consensus comparison
This set of UMAPs harmonizes cell type labels into broader groupings
to enable a comparison with consensus annotations. Note that the
Unknown cell type refers to cells uncategorized by
SingleR or scANVI.
ggplot(celltype_df) +
aes(x = UMAP1, y = UMAP2, color = label_recoded) +
geom_point(alpha = 0.5, size = 0.2) +
scale_color_manual(values = recoded_celltype_pal) +
facet_wrap(vars(celltype_method)) +
umap_theme +
guides(color = guide_legend(override.aes = list(size = 2, alpha = 1)))

We see broad compatibility among all three approaches, and the
Unknown consensus indeed appear to be mostly tumor cells (here,
Neuroendocrine). This is reassuring that we have some robust cell type
identifications here!
Heatmap
This section shows heatmaps comparing annotations to one another.
SingleR vs scANVI
This heatmap directly compares SingleR and
scANVI annotations.
# make a wide version for the heatmap
celltype_wide_df <- celltype_df |>
dplyr::filter(celltype_method != "validation_group_annotation") |>
dplyr::select(-label_recoded) |>
tidyr::pivot_wider(
names_from = celltype_method,
values_from = label
)
make_jaccard_heatmap(
celltype_wide_df,
"scanvi",
"singler",
"scANVI label",
"SingleR label"
)

This heatmap shows excellent correspondence between
SingleR and scANVI annotations with most of
the weight along the diagonal.
We’ll nail this down a bit further with some confusion matrix
metrics:
# ensure factors are compatible for caret
all_levels <- c(
celltype_wide_df$scanvi,
celltype_wide_df$singler
) |> unique()
caret::confusionMatrix(
factor(celltype_wide_df$scanvi, levels = all_levels),
factor(celltype_wide_df$singler, levels = all_levels)
)$overall
Accuracy Kappa AccuracyLower AccuracyUpper AccuracyNull AccuracyPValue
0.8372332 0.6317526 0.8355528 0.8389031 0.6887921 0.0000000
McnemarPValue
NaN
Harmonized labels for consensus comparison
This stacked heatmap compares all three methods to one another, using
broader cell type groupings to enable a more direct comparison with
consensus annotations.
# make a wide version for the heatmap
celltype_recoded_wide_df <- celltype_df |>
dplyr::select(-label) |>
tidyr::pivot_wider(
names_from = celltype_method,
values_from = label_recoded
)
list(
"SingleR annotations" = make_jaccard_matrix(celltype_recoded_wide_df, "validation_group_annotation", "singler"),
"scANVI annotations" = make_jaccard_matrix(celltype_recoded_wide_df, "validation_group_annotation", "scanvi")
) |>
purrr::imap(
\(jaccard_mat, name) create_single_heatmap(
mat = jaccard_mat,
row_title = name,
column_title = "Consensus validation groups",
keep_legend_name = "SingleR annotations" # arbitrary for labeling
)
) |>
# concatenate vertically into HeatmapList object
purrr::reduce(ComplexHeatmap::`%v%`) |>
ComplexHeatmap::draw(heatmap_legend_side = "right")

Again, we see excellent correspondence among all three methods.
Cross-library comparison
Across libraries, what fraction of SingleR and
scANVI labels exactly agree? For this, we’ll compare
among:
- Fraction of identical labels
- Fraction of labels that agree at least on the level of their cell
type “family,” where we define these families based on groups of NBAtlas
annotations:
- T cells:
T cell, Treg,
CD4+ T cell, CD8+ T cell
- Natural killer cells:
NK cell,
Circulating NK cell, Resident NK cell,
TOX2+/KIT+ NK cell
- Conventional dendritic cells (cDC):
cDC,
cDC1, cDC2/DC3,
Migratory cDC
- Monocytes:
Monocyte, Classical monocyte,
Patrolling monocyte
- Fraction of labels that entirely disagree
The table below shows these fractions for each library along with the
library size. We also visualize the relationship between library size
and family-based agreement.
# celltype_wide_df uses the original labels
identical_agree_df <- celltype_wide_df |>
dplyr::group_by(library_id) |>
dplyr::summarize(
frac_identical = sum(scanvi == singler)/dplyr::n(),
library_size = dplyr::n()
)
# create a wide data frame with "family" labels where possible
celltype_family_df <- celltype_df |>
dplyr::filter(celltype_method != "validation_group_annotation") |>
dplyr::left_join(
nbatlas_label_map, by = c("label" = "NBAtlas_label")
) |>
dplyr::select(cell_id, library_id, celltype_method, label) |>
tidyr::pivot_wider(
names_from = celltype_method,
values_from = label
)
family_agree_df <- celltype_family_df |>
dplyr::group_by(library_id) |>
dplyr::summarize(frac_same_family = sum(scanvi == singler)/dplyr::n())
# if families disagree, then there is no agreement at all
disagree_df <- celltype_family_df |>
dplyr::group_by(library_id) |>
dplyr::summarize(frac_disagree = sum(scanvi != singler)/dplyr::n())
# combine into a single table for display & viz
frac_df <- identical_agree_df |>
dplyr::left_join(family_agree_df) |>
dplyr::left_join(disagree_df) |>
# round to 4 decimals
dplyr::mutate(dplyr::across(contains("frac_"), \(x) round(x, 4))) |>
dplyr::select(
library_id,
library_size,
frac_identical,
frac_same_family,
frac_disagree
) |>
dplyr::arrange(library_size)
# use DT for improved exploration ability
DT::datatable(frac_df, rownames = FALSE)
# add PDX column for plotting
frac_df <- celltype_df |>
dplyr::select(library_id, is_xenograft) |>
unique() |>
dplyr::right_join(frac_df)
ggplot(frac_df) +
aes(x = library_size, y = frac_same_family) +
geom_point(aes(color = is_xenograft)) +
geom_smooth(method = "lm") +
labs(
x = "Library size",
y = "Fraction of SingleR/scANVI labels\nfrom the same family"
) +
# stop going above 1
scale_y_continuous(limits = c(0, 1))

Most cells have agreeing annotations in the majority of libraries.
Considering closely-related cells in the same family usually provides a
couple more cells to be annotated, but not a large percentage. There
appears to be a slight trend (not significant) that larger libraries
have more agreement between methods, but this also seems driven by the
libraries with low correspondence.
Libraries with high levels of disagreement
There are four libraries (2 PDX and 2 tissue) which show less than
50% agreement, so we should look at these a bit more closely. Notably,
these do not correspond to the four smallest libraries.
Here is the sample metadata for these four samples:
disagree_ids <- frac_df |>
dplyr::filter(frac_disagree >= 0.5) |>
dplyr::pull(library_id)
disagree_metadata <- sample_metadata |>
dplyr::filter(scpca_library_id %in% disagree_ids)
Looking closely here, we can see that these samples were in fact
derived from two participants where one sample is a xenograft and the
other is tissue.
disagree_metadata |>
dplyr::select(scpca_library_id, sample_type, participant_id) |>
dplyr::arrange(participant_id)
These participant IDs are not associated with any other samples:
sample_metadata |>
dplyr::filter(participant_id %in% disagree_metadata$participant_id) |>
dplyr::filter(!(scpca_library_id) %in% disagree_ids)
It’s possible that patient-specific biology explains the
discrepancies we see for these samples’ cell types.
We’ll look more closely at their disagreements as well with
heatmaps:
disagree_ids |>
# walk to only print the heatmaps once
purrr::walk(
\(id) {
library_df <- celltype_wide_df |>
dplyr::filter(library_id == id)
frac_disagree <- frac_df |>
dplyr::filter(library_id == id) |>
dplyr::pull(frac_disagree)
heatmap_title <- glue::glue("{id} (fraction disagree = {frac_disagree})")
make_jaccard_heatmap(
library_df,
"scanvi",
"singler",
glue::glue("{heatmap_title}\n\nscANVI label"),
"SingleR label"
)
})




For these libraries, the main source of disagreement is
scANVI applying more homogeneous labeling of
Neuroendocrine compared to SingleR which is
assigning more varied cell types. Note that the two libraries where
scANVI is only detecting
Neuroendocrine cells are the PDX libraries, for which we
expect lower cell type diversity in general. It’s not unlikely that a
lot of what SingleR has labeled Stromal other
or Schwann may be tumor.
Conclusions
Overall SingleR and scANVI appear to have
made highly similar predictions for the vast majority of libraries.
These cell types are also fairly similar to our existing consensus cell
types. Therefore, we can use agreement between SingleR and
scANVI to derive final annotations for this analysis
module.
sessionInfo()
R version 4.4.0 (2024-04-24)
Platform: aarch64-apple-darwin20
Running under: macOS 15.6
Matrix products: default
BLAS: /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libBLAS.dylib
LAPACK: /Library/Frameworks/R.framework/Versions/4.4-arm64/Resources/lib/libRlapack.dylib; LAPACK version 3.12.0
locale:
[1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8
time zone: America/New_York
tzcode source: internal
attached base packages:
[1] stats4 stats graphics grDevices datasets utils methods base
other attached packages:
[1] SingleCellExperiment_1.26.0 SummarizedExperiment_1.34.0 Biobase_2.64.0
[4] GenomicRanges_1.56.2 GenomeInfoDb_1.40.1 IRanges_2.38.1
[7] S4Vectors_0.42.1 BiocGenerics_0.50.0 MatrixGenerics_1.16.0
[10] matrixStats_1.5.0 patchwork_1.3.0 ggplot2_3.5.2
loaded via a namespace (and not attached):
[1] RColorBrewer_1.1-3 jsonlite_2.0.0 shape_1.4.6.1
[4] magrittr_2.0.3 farver_2.1.2 GlobalOptions_0.1.2
[7] zlibbioc_1.50.0 vctrs_0.6.5 Cairo_1.6-2
[10] DelayedMatrixStats_1.26.0 htmltools_0.5.8.1 S4Arrays_1.4.1
[13] forcats_1.0.0 curl_6.3.0 SparseArray_1.4.8
[16] pROC_1.18.5 caret_7.0-1 sass_0.4.10
[19] parallelly_1.45.0 bslib_0.9.0 htmlwidgets_1.6.4
[22] plyr_1.8.9 lubridate_1.9.4 cachem_1.1.0
[25] lifecycle_1.0.4 iterators_1.0.14 pkgconfig_2.0.3
[28] Matrix_1.7-1 R6_2.6.1 fastmap_1.2.0
[31] GenomeInfoDbData_1.2.12 future_1.58.0 clue_0.3-66
[34] digest_0.6.37 colorspace_2.1-1 rprojroot_2.0.4
[37] crosstalk_1.2.1 beachmat_2.20.0 labeling_0.4.3
[40] timechange_0.3.0 mgcv_1.9-1 httr_1.4.7
[43] abind_1.4-8 compiler_4.4.0 proxy_0.4-27
[46] bit64_4.6.0-1 withr_3.0.2 doParallel_1.0.17
[49] BiocParallel_1.38.0 MASS_7.3-64 lava_1.8.1
[52] DelayedArray_0.30.1 rjson_0.2.23 ModelMetrics_1.2.2.2
[55] tools_4.4.0 future.apply_1.20.0 nnet_7.3-20
[58] glue_1.8.0 nlme_3.1-166 grid_4.4.0
[61] cluster_2.1.8 reshape2_1.4.4 generics_0.1.4
[64] recipes_1.3.1 gtable_0.3.6 tzdb_0.5.0
[67] class_7.3-23 tidyr_1.3.1 data.table_1.17.4
[70] hms_1.1.3 XVector_0.44.0 foreach_1.5.2
[73] pillar_1.10.2 stringr_1.5.1 vroom_1.6.5
[76] circlize_0.4.16 splines_4.4.0 dplyr_1.1.4
[79] lattice_0.22-6 renv_1.1.3 survival_3.8-3
[82] bit_4.6.0 tidyselect_1.2.1 ComplexHeatmap_2.20.0
[85] scuttle_1.14.0 knitr_1.50 xfun_0.52
[88] hardhat_1.4.1 timeDate_4041.110 DT_0.33
[91] stringi_1.8.7 UCSC.utils_1.0.0 yaml_2.3.10
[94] evaluate_1.0.3 codetools_0.2-20 tibble_3.3.0
[97] BiocManager_1.30.26 cli_3.6.5 rpart_4.1.23
[100] jquerylib_0.1.4 Rcpp_1.0.14 globals_0.18.0
[103] png_0.1-8 parallel_4.4.0 gower_1.0.2
[106] readr_2.1.5 sparseMatrixStats_1.16.0 listenv_0.9.1
[109] ipred_0.9-15 scales_1.4.0 prodlim_2025.04.28
[112] ggridges_0.5.6 e1071_1.7-16 purrr_1.0.4
[115] crayon_1.5.3 GetoptLong_1.0.5 rlang_1.1.6
LS0tCnRpdGxlOiAiQ29tcGFyZSBzY0FOVkkgYW5kIFNpbmdsZVIgYW5ub3RhdGlvbnMiCmF1dGhvcjogIlN0ZXBoYW5pZSBKLiBTcGllbG1hbiIKZGF0ZTogImByIFN5cy5EYXRlKClgIgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2RlcHRoOiAzCiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUKLS0tCgpXZSBoYXZlIHVzZWQgYFNpbmdsZVJgIGFuZCBgc2NBTlZJL3NjQXJjaGVzYCB0byBwZXJmb3JtIGNlbGwgdHlwZSBhbm5vdGF0aW9uIG9uIGBTQ1BDUDAwMDAwNGAgc2FtcGxlcyB1c2luZyB0aGUgYE5CQXRsYXNgIHJlZmVyZW5jZS4KVGhlIGdvYWwgb2YgdGhpcyBub3RlYm9vayBpcyB0byBjb21wYXJlIHRoZXNlIHR3byBzZXRzIG9mIGluZmVyZW5jZXMgdG8gZGV0ZXJtaW5lIGlmIHRoZXkgZ2VuZXJhbGx5IGFncmVlLgogICAKIyMgU2V0dXAKCmBgYHtyLCB3YXJuaW5nID0gRkFMU0V9CnN1cHByZXNzUGFja2FnZVN0YXJ0dXBNZXNzYWdlcyh7CiAgbGlicmFyeShnZ3Bsb3QyKQogIGxpYnJhcnkocGF0Y2h3b3JrKQogIGxpYnJhcnkoU2luZ2xlQ2VsbEV4cGVyaW1lbnQpCn0pCgp0aGVtZV9zZXQodGhlbWVfYncoKSkKdW1hcF90aGVtZSA8LSBsaXN0KAogIHRoZW1lKAogICAgYXNwZWN0LnJhdGlvID0gMSwKICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iLCAKICAgIGF4aXMudGV4dCA9IGVsZW1lbnRfYmxhbmsoKSwgCiAgICBheGlzLnRpY2tzID0gZWxlbWVudF9ibGFuaygpCiAgKSAKKQpzZXQuc2VlZCgyMDI1KSAjIHVzZWQgZm9yIHNvbWUgdml6CgojIERlZmluZSBjb2xvciByYW1wIGZvciBzaGFyZWQgdXNlIGluIHRoZSBoZWF0bWFwcwpoZWF0bWFwX2NvbF9mdW4gPC0gY2lyY2xpemU6OmNvbG9yUmFtcDIoYygwLCAxKSwgY29sb3JzID0gYygid2hpdGUiLCAiZGFya3NsYXRlYmx1ZSIpKQojIFNldCBoZWF0bWFwIHBhZGRpbmcgb3B0aW9uCkNvbXBsZXhIZWF0bWFwOjpodF9vcHQoVElUTEVfUEFERElORyA9IGdyaWQ6OnVuaXQoMC42LCAiaW4iKSkKCiMgc2V0dGluZ3MKb3B0aW9ucygKICBkcGx5ci5zdW1tYXJpc2UuaW5mb3JtID0gRkFMU0UsIAogIHJlYWRyLnNob3dfY29sX3R5cGVzID0gRkFMU0UKKQoKCmBgYAoKCiMjIyBQYXRocwoKYGBge3IgYmFzZSBwYXRoc30KIyBUaGUgYmFzZSBwYXRoIGZvciB0aGUgT3BlblNjUENBIHJlcG9zaXRvcnksIGZvdW5kIGJ5IGl0cyAoaGlkZGVuKSAuZ2l0IGRpcmVjdG9yeQpyZXBvc2l0b3J5X2Jhc2UgPC0gcnByb2pyb290OjpmaW5kX3Jvb3QocnByb2pyb290Ojppc19naXRfcm9vdCkKCmRhdGFfZGlyIDwtIGZpbGUucGF0aChyZXBvc2l0b3J5X2Jhc2UsICJkYXRhIiwgImN1cnJlbnQiLCAiU0NQQ1AwMDAwMDQiKQptZXJnZWRfZGlyIDwtIGZpbGUucGF0aChyZXBvc2l0b3J5X2Jhc2UsICJkYXRhIiwgImN1cnJlbnQiLCAicmVzdWx0cyIsICJtZXJnZS1zY2UiLCAiU0NQQ1AwMDAwMDQiKQptb2R1bGVfZGlyIDwtIGZpbGUucGF0aChyZXBvc2l0b3J5X2Jhc2UsICJhbmFseXNlcyIsICJjZWxsLXR5cGUtbmV1cm9ibGFzdG9tYS0wNCIpCnJlZl9kaXIgPC0gZmlsZS5wYXRoKG1vZHVsZV9kaXIsICJyZWZlcmVuY2VzIikKcmVzdWx0c19kaXIgPC0gZmlsZS5wYXRoKG1vZHVsZV9kaXIsICJyZXN1bHRzIikKc2luZ2xlcl9kaXIgPC0gZmlsZS5wYXRoKHJlc3VsdHNfZGlyLCAic2luZ2xlciIpCnNjYW52aV9kaXIgPC0gZmlsZS5wYXRoKHJlc3VsdHNfZGlyLCAic2NhbnZpIikKYGBgCgoKYGBge3IgZmlsZSBwYXRoc30KIyBtZXJnZWQgU0NFIGZpbGUKc2NlX2ZpbGUgPC0gZmlsZS5wYXRoKAogIG1lcmdlZF9kaXIsCiAgIlNDUENQMDAwMDA0X21lcmdlZC5yZHMiCikKCiMgU2luZ2xlUiBmaWxlcwpzaW5nbGVyX2ZpbGVzIDwtIGxpc3QuZmlsZXMoCiAgcGF0aCA9IHNpbmdsZXJfZGlyLAogIHBhdHRlcm4gPSAiX3NpbmdsZXItYW5ub3RhdGlvbnNcXC50c3YiLAogIHJlY3Vyc2l2ZSA9IFRSVUUsCiAgZnVsbC5uYW1lcyA9IFRSVUUKKSB8PgogICMgYWRkIG5hbWVzIGFzIGxpYnJhcnkgaWQKICBwdXJycjo6c2V0X25hbWVzKAogICAgXCh4KSB7CiAgICAgIHN0cmluZ3I6OnN0cl9yZW1vdmUoYmFzZW5hbWUoeCksICJfc2luZ2xlci1hbm5vdGF0aW9ucy50c3YiKQogICAgfQogICkKCiMgc2NhbnZpIHByZWRpY3Rpb25zCnNjYW52aV9maWxlIDwtIGZpbGUucGF0aCgKICBzY2FudmlfZGlyLCAKICAic2NhbnZpLXByZWRpY3Rpb25zLnRzdiIKKQoKIyBicm9hZCBjb25zZW5zdXMgY2VsbCB0eXBlIGdyb3Vwcwp2YWxpZGF0aW9uX3VybCA8LSAiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL0FsZXhzTGVtb25hZGUvT3BlblNjUENBLWFuYWx5c2lzL3JlZnMvaGVhZHMvbWFpbi9hbmFseXNlcy9jZWxsLXR5cGUtY29uc2Vuc3VzL3JlZmVyZW5jZXMvY29uc2Vuc3VzLXZhbGlkYXRpb24tZ3JvdXBzLnRzdiIKCiMgcGFsZXR0ZSBmaWxlcwpyZWNvZGVkX3BhbGV0dGVfZmlsZSA8LSBmaWxlLnBhdGgoCiAgbW9kdWxlX2RpciwKICAicGFsZXR0ZXMiLAogICJoYXJtb25pemVkLWNlbGwtdHlwZS1wYWxldHRlLnRzdiIKKQpuYmF0bGFzX3BhbGV0dGVfZmlsZSA8LSBmaWxlLnBhdGgoCiAgbW9kdWxlX2RpciwKICAicGFsZXR0ZXMiLAogICJuYmF0bGFzLWNlbGwtdHlwZS1wYWxldHRlLnRzdiIKKQoKIyBsYWJlbCBtYXBwaW5nCm5iYXRsYXNfbGFiZWxfbWFwX2ZpbGUgPC0gZmlsZS5wYXRoKHJlZl9kaXIsICJuYmF0bGFzLWxhYmVsLW1hcC50c3YiKQoKIyBzYW1wbGUgbWV0YWRhdGEKc2FtcGxlX21ldGFkYXRhX2ZpbGUgPC0gZmlsZS5wYXRoKGRhdGFfZGlyLCAic2luZ2xlX2NlbGxfbWV0YWRhdGEudHN2IikKYGBgCgojIyMgRnVuY3Rpb25zCgpgYGB7cn0KIyBTb3VyY2UgSmFjY2FyZCBhbmQgaGVhdG1hcCB1dGlsaXRpZXMgZnVuY3Rpb25zCnNvdXJjZShmaWxlLnBhdGgobW9kdWxlX2RpciwgInNjcmlwdHMiLCAidXRpbHMiLCAiamFjY2FyZC11dGlscy5SIikpCgojIFNvdXJjZSBhZGRpdGlvbmFsIHV0aWxpdGllcyBmdW5jdGlvbnM6CiMgLSBzdWJzZXRfbmJhdGxhc19tYXJrZXJzKCkKIyAtIGhhcm1vbml6ZV9jZWxsdHlwZXMoKQojIC0gZmFjZXRlZF91bWFwKCkKIyAtIGdlbmVyYXRlX2RvdHBsb3QoKQpzb3VyY2UoZmlsZS5wYXRoKG1vZHVsZV9kaXIsICJzY3JpcHRzIiwgInV0aWxzIiwgImNlbGx0eXBlLXV0aWxzLlIiKSkKYGBgCgojIyMgUmVhZCBhbmQgcHJlcGFyZSBpbnB1dCBkYXRhCgpSZWFkIG1lcmdlZCBTQ0Ugb2JqZWN0OgoKYGBge3J9Cm1lcmdlZF9zY2UgPC0gcmVhZFJEUyhzY2VfZmlsZSkKIyBpbW1lZGlhdGVseSByZW1vdmUgYXNzYXlzIHdlIGRvbid0IG5lZWQgZm9yIHNwYWNlCmFzc2F5KG1lcmdlZF9zY2UsICJzcGxpY2VkIikgPC0gTlVMTAphc3NheShtZXJnZWRfc2NlLCAiY291bnRzIikgPC0gTlVMTApyZWR1Y2VkRGltKG1lcmdlZF9zY2UsICJQQ0EiKSA8LSBOVUxMCmBgYAoKUmVhZCBTaW5nbGVSIHJlc3VsdHM6CgpgYGB7cn0Kc2luZ2xlcl9kZiA8LSBzaW5nbGVyX2ZpbGVzIHw+CiAgcHVycnI6Om1hcChyZWFkcjo6cmVhZF90c3YpIHw+CiAgcHVycnI6Omxpc3RfcmJpbmQobmFtZXNfdG8gPSAibGlicmFyeV9pZCIpIHw+CiAgZHBseXI6Om11dGF0ZSgKICAgICMgYWRkIGNlbGwgaWQgY29sdW1uIGZvciB1bmlxdWUgcm93cyAmIGpvaW5pbmcKICAgIGNlbGxfaWQgPSBnbHVlOjpnbHVlKCJ7bGlicmFyeV9pZH0te2JhcmNvZGVzfSIpLAogICAgIyByZWNvZGUgTkEgLT4gInVua25vd25fc2luZ2xlciIKICAgIHNpbmdsZXIgPSBpZmVsc2UoaXMubmEocHJ1bmVkLmxhYmVscyksIlVua25vd24iLCBwcnVuZWQubGFiZWxzKQogICkgfD4KICBkcGx5cjo6c2VsZWN0KGNlbGxfaWQsIHNpbmdsZXIsIHNpbmdsZXJfZGVsdGFfbmV4dCA9IGRlbHRhLm5leHQpCmBgYAoKUmVhZCBhbmQgcHJlcGFyZSBzY2FudmkgcmVzdWx0czoKCmBgYHtyfQpzY2FudmlfZnVsbF9kZiA8LSByZWFkcjo6cmVhZF90c3YoCiAgZmlsZS5wYXRoKHNjYW52aV9kaXIsICJzY2FudmlfcHJlZGljdGlvbnMudHN2IikKKSB8PgogIGRwbHlyOjpzZWxlY3QoCiAgICBjZWxsX2lkLCAKICAgIHNjYW52aSA9IHNjYW52aV9wcmVkaWN0aW9uLCAKICAgIHN0YXJ0c193aXRoKCJwcF8iKQogICkgfD4KICB0aWR5cjo6cGl2b3RfbG9uZ2VyKAogICAgc3RhcnRzX3dpdGgoInBwXyIpLCAKICAgIG5hbWVzX3RvID0gInBvc3Rlcmlvcl9jZWxsdHlwZSIsIAogICAgdmFsdWVzX3RvID0gInBvc3RlcmlvciIKICApIHw+CiAgZHBseXI6Om11dGF0ZShwb3N0ZXJpb3JfY2VsbHR5cGUgPSBzdHJpbmdyOjpzdHJfcmVtb3ZlKHBvc3Rlcmlvcl9jZWxsdHlwZSwgIl5wcF8iKSkKCiMgY3JlYXRlIGRlZGljYXRlZCBjb2x1bW4gZm9yIHRoZSBwb3N0ZXJpb3IgYXNzb2NpYXRlZCB3aXRoIHRoZSBsYWJlbApzY2FudmlfZGYgPC0gc2NhbnZpX2Z1bGxfZGYgfD4KICBkcGx5cjo6ZmlsdGVyKHNjYW52aSA9PSBwb3N0ZXJpb3JfY2VsbHR5cGUpIHw+CiAgZHBseXI6OnNlbGVjdChjZWxsX2lkLCBzY2FudmksIHNjYW52aV9wb3N0ZXJpb3IgPSBwb3N0ZXJpb3IpCmBgYAoKClJlYWQgYW5kIHByZXBhcmUgYWRkaXRpb25hbCBoZWxwZXIgZmlsZXMgZm9yIHZpejoKCmBgYHtyfQojIHZhbGlkYXRpb24gZ3JvdXBzIGRhdGEgZnJhbWUKdmFsaWRhdGlvbl9kZiA8LSByZWFkcjo6cmVhZF90c3YodmFsaWRhdGlvbl91cmwpIHw+CiAgZHBseXI6OnNlbGVjdChjb25zZW5zdXNfYW5ub3RhdGlvbiwgdmFsaWRhdGlvbl9ncm91cF9hbm5vdGF0aW9uKQoKIyBzZXQgdXAgcGFsZXR0ZXMKcmVjb2RlZF9wYWxldHRlX2RmIDwtIHJlYWRyOjpyZWFkX3RzdihyZWNvZGVkX3BhbGV0dGVfZmlsZSkKcmVjb2RlZF9jZWxsdHlwZV9wYWwgPC0gcmVjb2RlZF9wYWxldHRlX2RmJGhleF9jb2xvcgpuYW1lcyhyZWNvZGVkX2NlbGx0eXBlX3BhbCkgPC0gcmVjb2RlZF9wYWxldHRlX2RmJGhhcm1vbml6ZWRfbGFiZWwKIyBmb3IgdGhpcyBub3RlYm9vaywgd2Ugd2FudCBqdXN0IGEgc2luZ2xlIFVua25vd24KcmVjb2RlZF9jZWxsdHlwZV9wYWxbIlVua25vd24iXSA8LSByZWNvZGVkX2NlbGx0eXBlX3BhbFsidW5rbm93bl9zaW5nbGVyIl0KCgpuYmF0bGFzX3BhbGV0dGVfZGYgPC0gcmVhZHI6OnJlYWRfdHN2KG5iYXRsYXNfcGFsZXR0ZV9maWxlKQpuYmF0bGFzX2NlbGx0eXBlX3BhbCA8LSBuYmF0bGFzX3BhbGV0dGVfZGYkaGV4X2NvbG9yCm5hbWVzKG5iYXRsYXNfY2VsbHR5cGVfcGFsKSA8LSBuYmF0bGFzX3BhbGV0dGVfZGYkTkJBdGxhc19sYWJlbAoKIyBzYW1wbGUgbWV0YWRhdGEKc2FtcGxlX21ldGFkYXRhIDwtIHJlYWRyOjpyZWFkX3RzdihzYW1wbGVfbWV0YWRhdGFfZmlsZSkKCiMgbGFiZWwgbWFwcGluZwpuYmF0bGFzX2xhYmVsX21hcCA8LSByZWFkcjo6cmVhZF90c3YobmJhdGxhc19sYWJlbF9tYXBfZmlsZSkKYGBgCgoKIyMgc2NBTlZJIHBvc3RlcmlvciBwcm9iYWJpbGl0aWVzCgpCZWZvcmUgY29tcGFyaW5nIHRvIGBTaW5nbGVSYCwgd2Ugc2hvdWxkIGdldCBhIHNlbnNlIG9mIHJlbGlhYmlsaXR5IGZvciB0aGUgYHNjQU5WSWAgcmVzdWx0cy4KYHNjQU5WSWAgcmV0dXJucyBhIGNlbGwtc3BlY2lmaWMgcG9zdGVyaW9yIHByb2JhYmlsaXR5IGZvciBlYWNoIGxhYmVsLCBzbyB3ZSdsbCBiZWdpbiBieSBsb29raW5nIGF0IHRoZXNlIGRpc3RyaWJ1dGlvbnMuCgpGaXJzdCwgd2hhdCBpcyB0aGUgZGlzdHJpYnV0aW9uIG9mIHBvc3RlcmlvciBwcm9iYWJpbGl0aWVzIGZvciBhc3NpZ25lZCBsYWJlbHM/CldlJ2xsIG1ha2UgYSBoaXN0b2dyYW0gYmVsb3cgdXNpbmcgYSB2ZXJ5IHZpc2libGUgY29sb3IgdG8gZW5zdXJlIHRoZSBwbG90IGlzIGxlZ2libGUsIHdoZXJlIGVhY2ggcGFuZWwgaXMgYSBsaWJyYXJ5LCBhbmQgbGlicmFyaWVzIGFyZSBvcmRlcmVkIGJ5IHNpemUuCgpgYGB7ciBmaWcuaGVpZ2h0PTEyLCBmaWcud2lkdGg9MTV9CnNjYW52aV9kZiB8PgogIHRpZHlyOjpzZXBhcmF0ZShjZWxsX2lkLCBpbnRvID0gYygibGlicmFyeV9pZCIsICJiYXJjb2RlIikpIHw+CiAgZHBseXI6OmFkZF9jb3VudChsaWJyYXJ5X2lkKSB8PgogIGRwbHlyOjptdXRhdGUoCiAgICBsaWJyYXJ5X2lkID0gZ2x1ZTo6Z2x1ZSgie2xpYnJhcnlfaWR9IChOPXtufSkiKSwKICAgIGxpYnJhcnlfaWQgPSBmYWN0b3IobGlicmFyeV9pZCksIAogICAgbGlicmFyeV9pZCA9IGZvcmNhdHM6OmZjdF9pbmZyZXEobGlicmFyeV9pZCksIAogICAgbGlicmFyeV9pZCA9IGZvcmNhdHM6OmZjdF9yZXYobGlicmFyeV9pZCkKICApIHw+CiAgIyMjIyMjIyMjIyMjIGludG8gZ2dwbG90ICMjIyMjIyMjIyMjIyMjIyMKICBnZ3Bsb3QoKSArIAogIGFlcyh4ID0gc2NhbnZpX3Bvc3RlcmlvcikgKyAKICAjIHVnbHkgYnV0IF92ZXJ5IHZpc2libGVfIGNvbG9yCiAgZ2VvbV9oaXN0b2dyYW0oYmlucyA9IDEwMCwgZmlsbCA9ICJtYWdlbnRhIiwgY29sb3IgPSAibWFnZW50YSIpICsgCiAgZmFjZXRfd3JhcCh2YXJzKGxpYnJhcnlfaWQpLCBzY2FsZXMgPSAiZnJlZV95IiwgbmNvbCA9IDcpCmBgYAoKCkFjcm9zcyBsaWJyYXJpZXMsIHRoZSBtZWRpYW4gcG9zdGVyaW9yIHByb2JhYmlsaXR5IGlzIGFsd2F5cyBleGNlcHRpb25hbGx5IGhpZ2ggd2hpY2ggc3VnZ2VzdHMgYHNjQU5WSWAgY29uZmlkZW5jZSBpcyByb3VnaGx5IHRoZSBzYW1lIGFjcm9zcyBsaWJyYXJpZXMuCgpMZXQncyBpbnN0ZWFkIGxvb2sgYXQgdGhlc2UgcHJvYmFiaWxpdGllcyBvbiBhIHBlci1jZWxsIHR5cGUgYmFzaXMsIHRoaXMgdGltZSB3aXRoIGEgcmlkZ2UgcGxvdCBpbmNsdWRpbmcgYSBsaW5lIGZvciB0aGUgbWVkaWFuIHZhbHVlIGZvciBlYWNoIGRpc3RyaWJ1dGlvbjoKCmBgYHtyIGZpZy5oZWlnaHQ9OCwgZmlnLndpZHRoPTh9CnNjYW52aV9yaWRnZXBsb3QgPC0gc2NhbnZpX2RmIHw+CiAgZHBseXI6Omdyb3VwX2J5KHNjYW52aSkgfD4KICBkcGx5cjo6bXV0YXRlKHNjYW52aSA9IGdsdWU6OmdsdWUoIntzY2Fudml9IChuPXtkcGx5cjo6bigpfSkiKSkgfD4KICAjIyMjIyMjIyMjIyBpbnRvIGdncGxvdCAjIyMjIyMjIyMjIyMKICBnZ3Bsb3QoKSArIAogICAgYWVzKHkgPSBmb3JjYXRzOjpmY3RfaW5mcmVxKHNjYW52aSksIHggPSBzY2FudmlfcG9zdGVyaW9yKSArIAogICAgIyBzaG93IHRoZSBtZWRpYW4KICAgIGdncmlkZ2VzOjpzdGF0X2RlbnNpdHlfcmlkZ2VzKHF1YW50aWxlX2xpbmVzID0gVFJVRSwgcXVhbnRpbGVzID0gMiwgYWxwaGEgPSAwLjcpICsKICAgIGxhYnMoeCA9ICJwb3N0ZXJpb3IgcHJvYmFiaWxpdHkiLCB5ID0gInNjQU5WSSBsYWJlbCAobnVtYmVyIG9mIGNlbGxzKSIpIApzY2FudmlfcmlkZ2VwbG90CmBgYAoKV2hpbGUgYW5ub3RhdGlvbiByZWxpYWJpbGl0eSBpcyBhYm91dCB0aGUgc2FtZSBvbiBhIHBlci1saWJyYXJ5IGJhc2lzLCBpdCBzZWVtcyB0aGF0IGNlcnRhaW4gY2VsbCB0eXBlcyBtYXkgYmUgbXVjaCBlYXNpZXIgdG8gY2xhc3NpZnkgdGhhbiBvdGhlcnMgd2hpY2ggbWF5IGJlIGEgZmFjdG9yIG9mIGhvdyB3ZWxsIHJlcHJlc2VudGVkIHRoZXkgYXJlIGluIHRoZSBgTkJBdGxhc2AgcmVmZXJlbmNlLgpUaGVyZSBhcmUgdmVyeSBmZXcgY2VsbHMgaW4gZ2VuZXJhbCBhc3NvY2lhdGVkIHdpdGggdGhlIGNlbGwgdHlwZXMgdGhhdCBoYXZlIHZlcnkgbG93IG1lZGlhbiBwb3N0ZXJpb3IgcHJvYmFiaWxpdGllcyAoYE5LVCBjZWxsYCwgYE1pZ3JhdG9yeSBjRENgLCBgQ2lyY3VsYXRpbmcgTksgY2VsbGApLgoKQSBgMC43NWAgdGhyZXNob2xkIHNlZW1zIGxpa2UgaXQgbWF5IGJlIGEgZ29vZCBtaWRkbGUtZ3JvdW5kIGZvciBwcmVzZXJ2aW5nIGxhYmVscyB3aGlsZSByZW1vdmluZyBsb3ctY29uZmlkZW5jZSBhbm5vdGF0aW9ucy4KV2hhdCBmcmFjdGlvbiBvZiBjZWxscyB3aWxsIHRoaXMgcmVtb3ZlPwoKYGBge3J9CnN1bShzY2FudmlfZGYkc2NhbnZpX3Bvc3RlcmlvciA8IDAuNzUpIC8gbnJvdyhzY2FudmlfZGYpCmBgYAoKV2hhdCBhcmUgdGhvc2UgY2VsbCB0eXBlcz8KCmBgYHtyfQpzY2FudmlfZGYgfD4KICBkcGx5cjo6ZmlsdGVyKHNjYW52aV9wb3N0ZXJpb3IgPCAwLjc1KSB8PgogIGRwbHlyOjpjb3VudChzY2FudmkpIHw+CiAgZHBseXI6OmFycmFuZ2UoZGVzYyhuKSkKYGBgCk5vdGFibHksIG5vbmUgYXJlIE5ldXJvZW5kb2NyaW5lICh0aGUgdHVtb3IgdHlwZSkuCgpXZSdsbCBhcHBseSB0aGUgMC43NSB0aHJlc2hvbGQgYmVsb3cgYnkgcmVjb2RpbmcgdGhlc2UgY2VsbHMgdG8gYFVua25vd25gLgoKYGBge3J9CnBwX3RocmVzaG9sZCA8LSAwLjc1CnNjYW52aV9kZiA8LSBzY2FudmlfZGYgfD4KICBkcGx5cjo6bXV0YXRlKHNjYW52aSA9IGlmZWxzZSgKICAgIHNjYW52aV9wb3N0ZXJpb3IgPCBwcF90aHJlc2hvbGQsIAogICAgIlVua25vd24iLAogICAgc2NhbnZpCiAgKSkKYGBgCgojIyBDb21wYXJlIGxhYmVscwoKV2UnbGwgY3JlYXRlIGEgc2luZ2xlIGRhdGEgZnJhbWUgd2l0aCBVTUFQIGNvb3JkaW5hdGVzIGFuZCBhbGwgY2VsbCB0eXBlcyAoY29uc2Vuc3VzLCBgU2luZ2xlUmAsIGFuZCBgc2NBTlZJYCkuIApXZSdsbCBkbyB0aGlzIGF0IHR3byBsZXZlbHMgb2Ygb3JnYW5pemF0aW9uOgoKLSBPbmUgd2l0aCBoYXJtb25pemVkIGxhYmVscyB0byBiZSBhYmxlIHRvIGNvbXBhcmUgdG8gY29uc2Vuc3VzCi0gT25lIHdpdGggb3JpZ2luYWwgTkJBdGxhcyBsYWJlbHMgdG8ganVzdCBjb21wYXJlIGBTaW5nbGVSYCB0byBgc2NBTlZJYAoKCmBgYHtyfQpjZWxsdHlwZV9kZiA8LSBzY3V0dGxlOjptYWtlUGVyQ2VsbERGKAogIG1lcmdlZF9zY2UsCiAgdXNlLmNvbGRhdGEgPSBjKCJzYW1wbGVfaWQiLCAiaXNfeGVub2dyYWZ0IiwgImNvbnNlbnN1c19jZWxsdHlwZV9hbm5vdGF0aW9uIiksCiAgdXNlLmRpbXJlZCA9IGMoIlVNQVAiKQopIHw+CiAgIyB0aGVyZSBBUkUgcmVwZWF0ZWQgYmFyY29kZXMgc28gd2UgbmVlZCB0byBrZWVwIGNlbGxfaWQgYXJvdW5kCiAgdGliYmxlOjpyb3duYW1lc190b19jb2x1bW4oImNlbGxfaWQiKSB8PgogIGRwbHlyOjpyZW5hbWUoCiAgICBVTUFQMSA9IFVNQVAuMSwKICAgIFVNQVAyID0gVU1BUC4yLAogICAgY29uc2Vuc3VzX2Fubm90YXRpb24gPSBjb25zZW5zdXNfY2VsbHR5cGVfYW5ub3RhdGlvbgogICkgfD4KICBkcGx5cjo6bGVmdF9qb2luKHZhbGlkYXRpb25fZGYsIGJ5ID0gImNvbnNlbnN1c19hbm5vdGF0aW9uIikgfD4KICAjIHJlY29kZSBjb25zZW5zdXMgTkFzIHRvICJVbmtub3duIgogIGRwbHlyOjptdXRhdGUoCiAgICB2YWxpZGF0aW9uX2dyb3VwX2Fubm90YXRpb24gPSBpZmVsc2UoCiAgICAgIGlzLm5hKHZhbGlkYXRpb25fZ3JvdXBfYW5ub3RhdGlvbiksICJVbmtub3duIiwgdmFsaWRhdGlvbl9ncm91cF9hbm5vdGF0aW9uCiAgICApCiAgKSB8PgogIGRwbHlyOjpzZWxlY3QoLWNvbnNlbnN1c19hbm5vdGF0aW9uKSB8PgogICMgbWVyZ2UgaW4gdGhlIGFubm90YXRpb25zCiAgZHBseXI6OmxlZnRfam9pbihzaW5nbGVyX2RmLCBieSA9ICJjZWxsX2lkIikgfD4KICBkcGx5cjo6bGVmdF9qb2luKHNjYW52aV9kZiwgYnkgPSAiY2VsbF9pZCIpIHw+CiAgIyBtYWtlIGl0IHRpZHkKICB0aWR5cjo6cGl2b3RfbG9uZ2VyKAogICAgYyh2YWxpZGF0aW9uX2dyb3VwX2Fubm90YXRpb24sIHNpbmdsZXIsIHNjYW52aSksIAogICAgbmFtZXNfdG8gPSAiY2VsbHR5cGVfbWV0aG9kIiwgCiAgICB2YWx1ZXNfdG8gPSAibGFiZWwiCiAgKSB8PgogIGhhcm1vbml6ZV9jZWxsdHlwZXMobGFiZWwsIGxhYmVsX3JlY29kZWQpIHw+CiAgZHBseXI6Om11dGF0ZShsYWJlbCA9IGlmZWxzZShsYWJlbCA9PSAiTkUiLCAiTmV1cm9lbmRvY3JpbmUiLCBsYWJlbCkpIHw+CiAgdGlkeXI6OnNlcGFyYXRlKGNlbGxfaWQsIGludG8gPSBjKCJsaWJyYXJ5X2lkIiwgImJhcmNvZGUiKSwgcmVtb3ZlID0gRkFMU0UpCmBgYAoKCiMjIFVNQVAKClRoaXMgc2VjdGlvbiBkaXNwbGF5cyB0aGUgVU1BUCBmb3IgdGhlIF9tZXJnZWQgb2JqZWN0XyAobm90IGludGVncmF0ZWQpIHdpdGggY2VsbCB0eXBlIGxhYmVscyBhY3Jvc3MgbWV0aG9kcy4KV2Ugc2hvdyBhIHBhbmVsIGZvciBlYWNoIGNlbGwgdHlwZSBtZXRob2QuCgojIyMgU2luZ2xlUiB2cyBzY0FOVkkKClRoaXMgc2V0IG9mIFVNQVBzIHNob3dzIGFubm90YXRpb24gcmVzdWx0cyBmcm9tIGBTaW5nbGVSYCBhbmQgYHNjQU5WSWAuCgpTaW5jZSB0aGVyZSBhcmUgbWFueSBjZWxsIHR5cGVzIGluIHRoaXMgcGxvdCwgaXQgaGFzIGJlZW4gY29sb3JlZCBzbyB0aGF0IGFsbCBjZWxsIHR5cGVzIGluIGEgZ2l2ZW4gImZhbWlseSIgZWFjaCBzaGFyZSBhIGNvbG9yLCB3aGVyZSB3ZSBjb25zaWRlciB0aGVzZSBmYW1pbHkgZ3JvdXBpbmdzOgoKKiBUIGNlbGxzCiogTmF0dXJhbCBraWxsZXIgY2VsbHMKKiBDb252ZW50aW9uYWwgZGVuZHJpdGljIGNlbGxzCiogTW9ub2N5dGVzCgpgYGB7ciwgZmlnLndpZHRoID0gMTIsIGZpZy5oZWlnaHQgPSA4fQpsYWJlbF9vcmRlciA8LSBuYmF0bGFzX2xhYmVsX21hcCB8PgogIGRwbHlyOjphZGRfY291bnQoTkJBdGxhc19mYW1pbHkpIHw+CiAgZHBseXI6OmFycmFuZ2UoZGVzYyhuKSwgTkJBdGxhc19mYW1pbHkpIHw+CiAgZHBseXI6OnB1bGwoTkJBdGxhc19sYWJlbCkKbGFiZWxfb3JkZXIgPC0gYyhsYWJlbF9vcmRlciwgIlVua25vd24iKQoKdW1hcF9kaXJlY3RfZGYgPC0gY2VsbHR5cGVfZGYgfD4KICBkcGx5cjo6ZmlsdGVyKGNlbGx0eXBlX21ldGhvZCAhPSAidmFsaWRhdGlvbl9ncm91cF9hbm5vdGF0aW9uIikgfD4KICAjIHNodWZmbGUgcG9pbnRzIHRvIGVuc3VyZSBvcmRlciB0aGV5IGFyZSBwbG90dGVkIGlzIHJhbmRvbQogIGRwbHlyOjpzYW1wbGVfZnJhYygxKSB8PiAKICAjIG9yZGVyIGNlbGx0eXBlcyBzbyB0aGF0IGZhbWlsaWVzIGFyZSBncm91cGVkIHRvZ2V0aGVyIGZvciBhIGNsZWFuZXIgbGVnZW5kCiAgIyBub3RlIHRoaXMgd2lsbCBnaXZlIHdhcm5pbmdzIGlmIGFueSBvZiB0aG9zZSBjZWxscyBhcmVuJ3QgaW4gdGhlIGFubm90YXRpb24sIGJ1dCB0aGF0J3MgZmluZQogIGRwbHlyOjptdXRhdGUobGFiZWwgPSBmYWN0b3IobGFiZWwsIGxldmVscyA9IGxhYmVsX29yZGVyKSkKICAKZ2dwbG90KHVtYXBfZGlyZWN0X2RmKSArCiAgYWVzKHggPSBVTUFQMSwgeSA9IFVNQVAyLCBjb2xvciA9IGxhYmVsKSArCiAgZ2VvbV9wb2ludChhbHBoYSA9IDAuNSwgc2l6ZSA9IDAuMikgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBuYmF0bGFzX2NlbGx0eXBlX3BhbCkgKwogIGZhY2V0X3dyYXAodmFycyhjZWxsdHlwZV9tZXRob2QpKSArCiAgdW1hcF90aGVtZSArCiAgZ3VpZGVzKGNvbG9yID0gZ3VpZGVfbGVnZW5kKG92ZXJyaWRlLmFlcyA9IGxpc3Qoc2l6ZSA9IDIsIGFscGhhID0gMSkpKQpgYGAKCkNlbGwgdHlwZSBhbm5vdGF0aW9ucyBsb29rIGJyb2FkbHkgY29uc2lzdGVudCBiZXR3ZWVuIHRoZXNlIG1ldGhvZHMsIGFsdGhvdWdoIGBTaW5nbGVSYCBhcHBlYXJzIHRvIGFzc2lnbiBtb3JlIGhldGVyb2dlbmVpdHkgd2l0aGluIHRoZSAiY2x1bXBzIiBvZiBjZWxscyAod2hpY2ggbGlrZWx5IGNvcnJlc3BvbmQgdG8gaW5kaXZpZHVhbCBsaWJyYXJpZXMgaW4gdGhpcyBtZXJnZWQgb2JqZWN0KS4KCgojIyMgSGFybW9uaXplZCBsYWJlbHMgZm9yIGNvbnNlbnN1cyBjb21wYXJpc29uCgpUaGlzIHNldCBvZiBVTUFQcyBoYXJtb25pemVzIGNlbGwgdHlwZSBsYWJlbHMgaW50byBicm9hZGVyIGdyb3VwaW5ncyB0byBlbmFibGUgYSBjb21wYXJpc29uIHdpdGggY29uc2Vuc3VzIGFubm90YXRpb25zLgpOb3RlIHRoYXQgdGhlIGBVbmtub3duYCBjZWxsIHR5cGUgcmVmZXJzIHRvIGNlbGxzIHVuY2F0ZWdvcml6ZWQgYnkgYFNpbmdsZVJgIG9yIGBzY0FOVklgLgoKYGBge3IsIGZpZy53aWR0aCA9IDEyLCBmaWcuaGVpZ2h0ID0gOH0KZ2dwbG90KGNlbGx0eXBlX2RmKSArCiAgYWVzKHggPSBVTUFQMSwgeSA9IFVNQVAyLCBjb2xvciA9IGxhYmVsX3JlY29kZWQpICsKICBnZW9tX3BvaW50KGFscGhhID0gMC41LCBzaXplID0gMC4yKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IHJlY29kZWRfY2VsbHR5cGVfcGFsKSArCiAgZmFjZXRfd3JhcCh2YXJzKGNlbGx0eXBlX21ldGhvZCkpICsKICB1bWFwX3RoZW1lICsKICBndWlkZXMoY29sb3IgPSBndWlkZV9sZWdlbmQob3ZlcnJpZGUuYWVzID0gbGlzdChzaXplID0gMiwgYWxwaGEgPSAxKSkpCmBgYApXZSBzZWUgYnJvYWQgY29tcGF0aWJpbGl0eSBhbW9uZyBhbGwgdGhyZWUgYXBwcm9hY2hlcywgYW5kIHRoZSBVbmtub3duIGNvbnNlbnN1cyBpbmRlZWQgYXBwZWFyIHRvIGJlIG1vc3RseSB0dW1vciBjZWxscyAoaGVyZSwgTmV1cm9lbmRvY3JpbmUpLgpUaGlzIGlzIHJlYXNzdXJpbmcgdGhhdCB3ZSBoYXZlIHNvbWUgcm9idXN0IGNlbGwgdHlwZSBpZGVudGlmaWNhdGlvbnMgaGVyZSEKCgojIyBIZWF0bWFwCgpUaGlzIHNlY3Rpb24gc2hvd3MgaGVhdG1hcHMgY29tcGFyaW5nIGFubm90YXRpb25zIHRvIG9uZSBhbm90aGVyLgoKCiMjIyBTaW5nbGVSIHZzIHNjQU5WSQoKVGhpcyBoZWF0bWFwIGRpcmVjdGx5IGNvbXBhcmVzIGBTaW5nbGVSYCBhbmQgYHNjQU5WSWAgYW5ub3RhdGlvbnMuCgpgYGB7ciwgZmlnLmhlaWdodCA9IDksIGZpZy53aWR0aCA9IDl9IAojIG1ha2UgYSB3aWRlIHZlcnNpb24gZm9yIHRoZSBoZWF0bWFwCmNlbGx0eXBlX3dpZGVfZGYgPC0gY2VsbHR5cGVfZGYgfD4KICBkcGx5cjo6ZmlsdGVyKGNlbGx0eXBlX21ldGhvZCAhPSAidmFsaWRhdGlvbl9ncm91cF9hbm5vdGF0aW9uIikgfD4KICBkcGx5cjo6c2VsZWN0KC1sYWJlbF9yZWNvZGVkKSB8PgogIHRpZHlyOjpwaXZvdF93aWRlcigKICAgIG5hbWVzX2Zyb20gPSBjZWxsdHlwZV9tZXRob2QsCiAgICB2YWx1ZXNfZnJvbSA9IGxhYmVsCiAgKQoKbWFrZV9qYWNjYXJkX2hlYXRtYXAoCiAgY2VsbHR5cGVfd2lkZV9kZiwKICAic2NhbnZpIiwKICAic2luZ2xlciIsCiAgInNjQU5WSSBsYWJlbCIsCiAgIlNpbmdsZVIgbGFiZWwiCikKYGBgCgpUaGlzIGhlYXRtYXAgc2hvd3MgZXhjZWxsZW50IGNvcnJlc3BvbmRlbmNlIGJldHdlZW4gYFNpbmdsZVJgIGFuZCBgc2NBTlZJYCBhbm5vdGF0aW9ucyB3aXRoIG1vc3Qgb2YgdGhlIHdlaWdodCBhbG9uZyB0aGUgZGlhZ29uYWwuCgpXZSdsbCBuYWlsIHRoaXMgZG93biBhIGJpdCBmdXJ0aGVyIHdpdGggc29tZSBjb25mdXNpb24gbWF0cml4IG1ldHJpY3M6CgpgYGB7ciwgbWVzc2FnZT1GQUxTRX0KIyBlbnN1cmUgZmFjdG9ycyBhcmUgY29tcGF0aWJsZSBmb3IgY2FyZXQKYWxsX2xldmVscyA8LSBjKAogIGNlbGx0eXBlX3dpZGVfZGYkc2NhbnZpLCAKICBjZWxsdHlwZV93aWRlX2RmJHNpbmdsZXIKKSB8PiB1bmlxdWUoKQoKY2FyZXQ6OmNvbmZ1c2lvbk1hdHJpeCgKICBmYWN0b3IoY2VsbHR5cGVfd2lkZV9kZiRzY2FudmksIGxldmVscyA9IGFsbF9sZXZlbHMpLCAgCiAgZmFjdG9yKGNlbGx0eXBlX3dpZGVfZGYkc2luZ2xlciwgbGV2ZWxzID0gYWxsX2xldmVscykKKSRvdmVyYWxsCgpgYGAKCiMjIyBIYXJtb25pemVkIGxhYmVscyBmb3IgY29uc2Vuc3VzIGNvbXBhcmlzb24KClRoaXMgc3RhY2tlZCBoZWF0bWFwIGNvbXBhcmVzIGFsbCB0aHJlZSBtZXRob2RzIHRvIG9uZSBhbm90aGVyLCB1c2luZyBicm9hZGVyIGNlbGwgdHlwZSBncm91cGluZ3MgdG8gZW5hYmxlIGEgbW9yZSBkaXJlY3QgY29tcGFyaXNvbiB3aXRoIGNvbnNlbnN1cyBhbm5vdGF0aW9ucy4KCmBgYHtyLCBmaWcuaGVpZ2h0ID0gMTMsIGZpZy53aWR0aCA9IDEyfSAKIyBtYWtlIGEgd2lkZSB2ZXJzaW9uIGZvciB0aGUgaGVhdG1hcApjZWxsdHlwZV9yZWNvZGVkX3dpZGVfZGYgPC0gY2VsbHR5cGVfZGYgfD4KICBkcGx5cjo6c2VsZWN0KC1sYWJlbCkgfD4KICB0aWR5cjo6cGl2b3Rfd2lkZXIoCiAgICBuYW1lc19mcm9tID0gY2VsbHR5cGVfbWV0aG9kLAogICAgdmFsdWVzX2Zyb20gPSBsYWJlbF9yZWNvZGVkCiAgKQoKbGlzdCgKICAiU2luZ2xlUiBhbm5vdGF0aW9ucyIgPSBtYWtlX2phY2NhcmRfbWF0cml4KGNlbGx0eXBlX3JlY29kZWRfd2lkZV9kZiwgInZhbGlkYXRpb25fZ3JvdXBfYW5ub3RhdGlvbiIsICJzaW5nbGVyIiksCiAgInNjQU5WSSBhbm5vdGF0aW9ucyIgPSBtYWtlX2phY2NhcmRfbWF0cml4KGNlbGx0eXBlX3JlY29kZWRfd2lkZV9kZiwgInZhbGlkYXRpb25fZ3JvdXBfYW5ub3RhdGlvbiIsICJzY2FudmkiKQopIHw+CiAgcHVycnI6OmltYXAoCiAgICBcKGphY2NhcmRfbWF0LCBuYW1lKSBjcmVhdGVfc2luZ2xlX2hlYXRtYXAoCiAgICAgICAgbWF0ID0gamFjY2FyZF9tYXQsCiAgICAgICAgcm93X3RpdGxlID0gbmFtZSwKICAgICAgICBjb2x1bW5fdGl0bGUgPSAiQ29uc2Vuc3VzIHZhbGlkYXRpb24gZ3JvdXBzIiwKICAgICAgICBrZWVwX2xlZ2VuZF9uYW1lID0gIlNpbmdsZVIgYW5ub3RhdGlvbnMiICMgYXJiaXRyYXJ5IGZvciBsYWJlbGluZwogICAgKQogICkgfD4KICAjIGNvbmNhdGVuYXRlIHZlcnRpY2FsbHkgaW50byBIZWF0bWFwTGlzdCBvYmplY3QKICBwdXJycjo6cmVkdWNlKENvbXBsZXhIZWF0bWFwOjpgJXYlYCkgfD4KICBDb21wbGV4SGVhdG1hcDo6ZHJhdyhoZWF0bWFwX2xlZ2VuZF9zaWRlID0gInJpZ2h0IikKYGBgCgpBZ2Fpbiwgd2Ugc2VlIGV4Y2VsbGVudCBjb3JyZXNwb25kZW5jZSBhbW9uZyBhbGwgdGhyZWUgbWV0aG9kcy4KCgoKIyMgQ3Jvc3MtbGlicmFyeSBjb21wYXJpc29uCgpBY3Jvc3MgbGlicmFyaWVzLCB3aGF0IGZyYWN0aW9uIG9mIGBTaW5nbGVSYCBhbmQgYHNjQU5WSWAgbGFiZWxzIGV4YWN0bHkgYWdyZWU/CkZvciB0aGlzLCB3ZSdsbCBjb21wYXJlIGFtb25nOgoKKiBGcmFjdGlvbiBvZiBpZGVudGljYWwgbGFiZWxzCiogRnJhY3Rpb24gb2YgbGFiZWxzIHRoYXQgYWdyZWUgYXQgbGVhc3Qgb24gdGhlIGxldmVsIG9mIHRoZWlyIGNlbGwgdHlwZSAiZmFtaWx5LCIgd2hlcmUgd2UgZGVmaW5lIHRoZXNlIGZhbWlsaWVzIGJhc2VkIG9uIGdyb3VwcyBvZiBOQkF0bGFzIGFubm90YXRpb25zOgogICogVCBjZWxsczogYFQgY2VsbGAsIGBUcmVnYCwgYENENCsgVCBjZWxsYCwgYENEOCsgVCBjZWxsYAogICogTmF0dXJhbCBraWxsZXIgY2VsbHM6IGBOSyBjZWxsYCwgYENpcmN1bGF0aW5nIE5LIGNlbGxgLCBgUmVzaWRlbnQgTksgY2VsbGAsIGBUT1gyKy9LSVQrIE5LIGNlbGxgCiAgKiBDb252ZW50aW9uYWwgZGVuZHJpdGljIGNlbGxzIChjREMpOiBgY0RDYCwgYGNEQzFgLCBgY0RDMi9EQzNgLCBgTWlncmF0b3J5IGNEQ2AKICAqIE1vbm9jeXRlczogYE1vbm9jeXRlYCwgYENsYXNzaWNhbCBtb25vY3l0ZWAsIGBQYXRyb2xsaW5nIG1vbm9jeXRlYAoqIEZyYWN0aW9uIG9mIGxhYmVscyB0aGF0IGVudGlyZWx5IGRpc2FncmVlCgpUaGUgdGFibGUgYmVsb3cgc2hvd3MgdGhlc2UgZnJhY3Rpb25zIGZvciBlYWNoIGxpYnJhcnkgYWxvbmcgd2l0aCB0aGUgbGlicmFyeSBzaXplLgpXZSBhbHNvIHZpc3VhbGl6ZSB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gbGlicmFyeSBzaXplIGFuZCBmYW1pbHktYmFzZWQgYWdyZWVtZW50LgoKYGBge3J9CiMgY2VsbHR5cGVfd2lkZV9kZiB1c2VzIHRoZSBvcmlnaW5hbCBsYWJlbHMKaWRlbnRpY2FsX2FncmVlX2RmIDwtIGNlbGx0eXBlX3dpZGVfZGYgfD4KICBkcGx5cjo6Z3JvdXBfYnkobGlicmFyeV9pZCkgfD4KICBkcGx5cjo6c3VtbWFyaXplKAogICAgZnJhY19pZGVudGljYWwgPSBzdW0oc2NhbnZpID09IHNpbmdsZXIpL2RwbHlyOjpuKCksIAogICAgbGlicmFyeV9zaXplID0gZHBseXI6Om4oKQogICkKCiMgY3JlYXRlIGEgd2lkZSBkYXRhIGZyYW1lIHdpdGggImZhbWlseSIgbGFiZWxzIHdoZXJlIHBvc3NpYmxlCmNlbGx0eXBlX2ZhbWlseV9kZiA8LSBjZWxsdHlwZV9kZiB8PgogIGRwbHlyOjpmaWx0ZXIoY2VsbHR5cGVfbWV0aG9kICE9ICJ2YWxpZGF0aW9uX2dyb3VwX2Fubm90YXRpb24iKSB8PgogIGRwbHlyOjpsZWZ0X2pvaW4oCiAgICBuYmF0bGFzX2xhYmVsX21hcCwgYnkgPSBjKCJsYWJlbCIgPSAiTkJBdGxhc19sYWJlbCIpCiAgKSB8PgogIGRwbHlyOjpzZWxlY3QoY2VsbF9pZCwgbGlicmFyeV9pZCwgY2VsbHR5cGVfbWV0aG9kLCBsYWJlbCkgfD4KICB0aWR5cjo6cGl2b3Rfd2lkZXIoCiAgICBuYW1lc19mcm9tID0gY2VsbHR5cGVfbWV0aG9kLAogICAgdmFsdWVzX2Zyb20gPSBsYWJlbAogICkKCmZhbWlseV9hZ3JlZV9kZiA8LSBjZWxsdHlwZV9mYW1pbHlfZGYgfD4KICBkcGx5cjo6Z3JvdXBfYnkobGlicmFyeV9pZCkgfD4KICBkcGx5cjo6c3VtbWFyaXplKGZyYWNfc2FtZV9mYW1pbHkgPSBzdW0oc2NhbnZpID09IHNpbmdsZXIpL2RwbHlyOjpuKCkpCgojIGlmIGZhbWlsaWVzIGRpc2FncmVlLCB0aGVuIHRoZXJlIGlzIG5vIGFncmVlbWVudCBhdCBhbGwKZGlzYWdyZWVfZGYgPC0gY2VsbHR5cGVfZmFtaWx5X2RmIHw+CiAgZHBseXI6Omdyb3VwX2J5KGxpYnJhcnlfaWQpIHw+CiAgZHBseXI6OnN1bW1hcml6ZShmcmFjX2Rpc2FncmVlID0gc3VtKHNjYW52aSAhPSBzaW5nbGVyKS9kcGx5cjo6bigpKQpgYGAKCgpgYGB7ciwgbWVzc2FnZSA9IEZBTFNFfQojIGNvbWJpbmUgaW50byBhIHNpbmdsZSB0YWJsZSBmb3IgZGlzcGxheSAmIHZpegpmcmFjX2RmIDwtIGlkZW50aWNhbF9hZ3JlZV9kZiB8PgogIGRwbHlyOjpsZWZ0X2pvaW4oZmFtaWx5X2FncmVlX2RmKSB8PgogIGRwbHlyOjpsZWZ0X2pvaW4oZGlzYWdyZWVfZGYpIHw+CiAgIyByb3VuZCB0byA0IGRlY2ltYWxzCiAgZHBseXI6Om11dGF0ZShkcGx5cjo6YWNyb3NzKGNvbnRhaW5zKCJmcmFjXyIpLCBcKHgpIHJvdW5kKHgsIDQpKSkgfD4KICBkcGx5cjo6c2VsZWN0KAogICAgbGlicmFyeV9pZCwgCiAgICBsaWJyYXJ5X3NpemUsIAogICAgZnJhY19pZGVudGljYWwsIAogICAgZnJhY19zYW1lX2ZhbWlseSwgCiAgICBmcmFjX2Rpc2FncmVlCiAgKSB8PgogIGRwbHlyOjphcnJhbmdlKGxpYnJhcnlfc2l6ZSkKCiMgdXNlIERUIGZvciBpbXByb3ZlZCBleHBsb3JhdGlvbiBhYmlsaXR5CkRUOjpkYXRhdGFibGUoZnJhY19kZiwgcm93bmFtZXMgPSBGQUxTRSkKYGBgCgpgYGB7ciwgbWVzc2FnZSA9IEZBTFNFLCBmaWcud2lkdGggPSA1LCBmaWcuaGVpZ2h0ID0gM30KIyBhZGQgUERYIGNvbHVtbiBmb3IgcGxvdHRpbmcKZnJhY19kZiA8LSBjZWxsdHlwZV9kZiB8PiAKICBkcGx5cjo6c2VsZWN0KGxpYnJhcnlfaWQsIGlzX3hlbm9ncmFmdCkgfD4KICB1bmlxdWUoKSB8PgogIGRwbHlyOjpyaWdodF9qb2luKGZyYWNfZGYpCgpnZ3Bsb3QoZnJhY19kZikgKyAKICBhZXMoeCA9IGxpYnJhcnlfc2l6ZSwgeSA9IGZyYWNfc2FtZV9mYW1pbHkpICsgCiAgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBpc194ZW5vZ3JhZnQpKSArIAogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIpICsKICBsYWJzKAogICAgeCA9ICJMaWJyYXJ5IHNpemUiLAogICAgeSA9ICJGcmFjdGlvbiBvZiBTaW5nbGVSL3NjQU5WSSBsYWJlbHNcbmZyb20gdGhlIHNhbWUgZmFtaWx5IgogICkgKwogICMgc3RvcCBnb2luZyBhYm92ZSAxCiAgc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cyA9IGMoMCwgMSkpCmBgYAoKTW9zdCBjZWxscyBoYXZlIGFncmVlaW5nIGFubm90YXRpb25zIGluIHRoZSBtYWpvcml0eSBvZiBsaWJyYXJpZXMuCkNvbnNpZGVyaW5nIGNsb3NlbHktcmVsYXRlZCBjZWxscyBpbiB0aGUgc2FtZSBmYW1pbHkgdXN1YWxseSBwcm92aWRlcyBhIGNvdXBsZSBtb3JlIGNlbGxzIHRvIGJlIGFubm90YXRlZCwgYnV0IG5vdCBhIGxhcmdlIHBlcmNlbnRhZ2UuClRoZXJlIGFwcGVhcnMgdG8gYmUgYSBzbGlnaHQgdHJlbmQgKG5vdCBzaWduaWZpY2FudCkgdGhhdCBsYXJnZXIgbGlicmFyaWVzIGhhdmUgbW9yZSBhZ3JlZW1lbnQgYmV0d2VlbiBtZXRob2RzLCBidXQgdGhpcyBhbHNvIHNlZW1zIGRyaXZlbiBieSB0aGUgbGlicmFyaWVzIHdpdGggbG93IGNvcnJlc3BvbmRlbmNlLgoKIyMjIExpYnJhcmllcyB3aXRoIGhpZ2ggbGV2ZWxzIG9mIGRpc2FncmVlbWVudCAKClRoZXJlIGFyZSBmb3VyIGxpYnJhcmllcyAoMiBQRFggYW5kIDIgdGlzc3VlKSB3aGljaCBzaG93IGxlc3MgdGhhbiA1MCUgYWdyZWVtZW50LCBzbyB3ZSBzaG91bGQgbG9vayBhdCB0aGVzZSBhIGJpdCBtb3JlIGNsb3NlbHkuCk5vdGFibHksIHRoZXNlIGRvIF9ub3RfIGNvcnJlc3BvbmQgdG8gdGhlIGZvdXIgc21hbGxlc3QgbGlicmFyaWVzLgoKSGVyZSBpcyB0aGUgc2FtcGxlIG1ldGFkYXRhIGZvciB0aGVzZSBmb3VyIHNhbXBsZXM6CgpgYGB7cn0KZGlzYWdyZWVfaWRzIDwtIGZyYWNfZGYgfD4KICBkcGx5cjo6ZmlsdGVyKGZyYWNfZGlzYWdyZWUgPj0gMC41KSB8PgogIGRwbHlyOjpwdWxsKGxpYnJhcnlfaWQpCgpkaXNhZ3JlZV9tZXRhZGF0YSA8LSBzYW1wbGVfbWV0YWRhdGEgfD4KICBkcGx5cjo6ZmlsdGVyKHNjcGNhX2xpYnJhcnlfaWQgJWluJSBkaXNhZ3JlZV9pZHMpCmBgYAoKTG9va2luZyBjbG9zZWx5IGhlcmUsIHdlIGNhbiBzZWUgdGhhdCB0aGVzZSBzYW1wbGVzIHdlcmUgaW4gZmFjdCBkZXJpdmVkIGZyb20gdHdvIHBhcnRpY2lwYW50cyB3aGVyZSBvbmUgc2FtcGxlIGlzIGEgeGVub2dyYWZ0IGFuZCB0aGUgb3RoZXIgaXMgdGlzc3VlLgoKYGBge3J9CmRpc2FncmVlX21ldGFkYXRhIHw+CiAgZHBseXI6OnNlbGVjdChzY3BjYV9saWJyYXJ5X2lkLCBzYW1wbGVfdHlwZSwgcGFydGljaXBhbnRfaWQpIHw+CiAgZHBseXI6OmFycmFuZ2UocGFydGljaXBhbnRfaWQpCmBgYAoKVGhlc2UgcGFydGljaXBhbnQgSURzIGFyZSBub3QgYXNzb2NpYXRlZCB3aXRoIGFueSBvdGhlciBzYW1wbGVzOgoKYGBge3J9CnNhbXBsZV9tZXRhZGF0YSB8PgogIGRwbHlyOjpmaWx0ZXIocGFydGljaXBhbnRfaWQgJWluJSBkaXNhZ3JlZV9tZXRhZGF0YSRwYXJ0aWNpcGFudF9pZCkgfD4KICBkcGx5cjo6ZmlsdGVyKCEoc2NwY2FfbGlicmFyeV9pZCkgJWluJSBkaXNhZ3JlZV9pZHMpCmBgYAoKSXQncyBwb3NzaWJsZSB0aGF0IHBhdGllbnQtc3BlY2lmaWMgYmlvbG9neSBleHBsYWlucyB0aGUgZGlzY3JlcGFuY2llcyB3ZSBzZWUgZm9yIHRoZXNlIHNhbXBsZXMnIGNlbGwgdHlwZXMuCgpXZSdsbCBsb29rIG1vcmUgY2xvc2VseSBhdCB0aGVpciBkaXNhZ3JlZW1lbnRzIGFzIHdlbGwgd2l0aCBoZWF0bWFwczoKCmBgYHtyLCBmaWcuaGVpZ2h0ID0gOCwgZmlnLndpZHRoID0gOH0gCmRpc2FncmVlX2lkcyB8PgogICMgd2FsayB0byBvbmx5IHByaW50IHRoZSBoZWF0bWFwcyBvbmNlCiAgcHVycnI6OndhbGsoCiAgICBcKGlkKSB7CiAgICAgIGxpYnJhcnlfZGYgPC0gY2VsbHR5cGVfd2lkZV9kZiB8PgogICAgICAgIGRwbHlyOjpmaWx0ZXIobGlicmFyeV9pZCA9PSBpZCkKICAgICAgCiAgICAgZnJhY19kaXNhZ3JlZSA8LSBmcmFjX2RmIHw+CiAgICAgICBkcGx5cjo6ZmlsdGVyKGxpYnJhcnlfaWQgPT0gaWQpIHw+CiAgICAgICBkcGx5cjo6cHVsbChmcmFjX2Rpc2FncmVlKQogICAgICAgCiAgICAgCiAgICAgaGVhdG1hcF90aXRsZSA8LSBnbHVlOjpnbHVlKCJ7aWR9IChmcmFjdGlvbiBkaXNhZ3JlZSA9IHtmcmFjX2Rpc2FncmVlfSkiKQogICAgICAKICAgICAgbWFrZV9qYWNjYXJkX2hlYXRtYXAoCiAgICAgICAgbGlicmFyeV9kZiwKICAgICAgICAic2NhbnZpIiwKICAgICAgICAic2luZ2xlciIsCiAgICAgICAgZ2x1ZTo6Z2x1ZSgie2hlYXRtYXBfdGl0bGV9XG5cbnNjQU5WSSBsYWJlbCIpLAogICAgICAgICJTaW5nbGVSIGxhYmVsIgogICAgICApCiAgfSkKYGBgCgoKRm9yIHRoZXNlIGxpYnJhcmllcywgdGhlIG1haW4gc291cmNlIG9mIGRpc2FncmVlbWVudCBpcyBgc2NBTlZJYCBhcHBseWluZyBtb3JlIGhvbW9nZW5lb3VzIGxhYmVsaW5nIG9mIGBOZXVyb2VuZG9jcmluZWAgY29tcGFyZWQgdG8gYFNpbmdsZVJgIHdoaWNoIGlzIGFzc2lnbmluZyBtb3JlIHZhcmllZCBjZWxsIHR5cGVzLgpOb3RlIHRoYXQgdGhlIHR3byBsaWJyYXJpZXMgd2hlcmUgYHNjQU5WSWAgaXMgX29ubHlfIGRldGVjdGluZyBgTmV1cm9lbmRvY3JpbmVgIGNlbGxzIGFyZSB0aGUgUERYIGxpYnJhcmllcywgZm9yIHdoaWNoIHdlIGV4cGVjdCBsb3dlciBjZWxsIHR5cGUgZGl2ZXJzaXR5IGluIGdlbmVyYWwuCkl0J3Mgbm90IHVubGlrZWx5IHRoYXQgYSBsb3Qgb2Ygd2hhdCBgU2luZ2xlUmAgaGFzIGxhYmVsZWQgYFN0cm9tYWwgb3RoZXJgIG9yIGBTY2h3YW5uYCBtYXkgYmUgdHVtb3IuCgoKIyMgQ29uY2x1c2lvbnMKCk92ZXJhbGwgYFNpbmdsZVJgIGFuZCBgc2NBTlZJYCBhcHBlYXIgdG8gaGF2ZSBtYWRlIGhpZ2hseSBzaW1pbGFyIHByZWRpY3Rpb25zIGZvciB0aGUgdmFzdCBtYWpvcml0eSBvZiBsaWJyYXJpZXMuClRoZXNlIGNlbGwgdHlwZXMgYXJlIGFsc28gZmFpcmx5IHNpbWlsYXIgdG8gb3VyIGV4aXN0aW5nIGNvbnNlbnN1cyBjZWxsIHR5cGVzLgpUaGVyZWZvcmUsIHdlIGNhbiB1c2UgYWdyZWVtZW50IGJldHdlZW4gYFNpbmdsZVJgIGFuZCBgc2NBTlZJYCB0byBkZXJpdmUgZmluYWwgYW5ub3RhdGlvbnMgZm9yIHRoaXMgYW5hbHlzaXMgbW9kdWxlLgoKCmBgYHtyfQpzZXNzaW9uSW5mbygpCmBgYAo=